Skip to content

ratelimit: add support for x-ratelimit-* headers in local rate limiting#18157

Merged
adisuissa merged 56 commits intoenvoyproxy:mainfrom
zhxie:header-lrl
Mar 10, 2022
Merged

ratelimit: add support for x-ratelimit-* headers in local rate limiting#18157
adisuissa merged 56 commits intoenvoyproxy:mainfrom
zhxie:header-lrl

Conversation

@zhxie
Copy link
Copy Markdown
Contributor

@zhxie zhxie commented Sep 17, 2021

Signed-off-by: Xie Zhihao zhihao.xie@intel.com

Commit Message: ratelimit: add support for x-ratelimit-* headers in local rate limiting
Additional Description:
Risk Level: Low
Testing: Unit
Docs Changes: N/A
Release Notes: Added
Platform Specific Features: N/A
Fixes #15293
Deprecated: the envoy::extensions::filters::http::ratelimit::v3::RateLimit::XRateLimitHeadersRFCVersion is deprecated in favor of envoy::extensions::common::ratelimit::v3::XRateLimitHeadersRFCVersion

@repokitteh-read-only
Copy link
Copy Markdown

As a reminder, PRs marked as draft will not be automatically assigned reviewers,
or be handled by maintainer-oncall triage.

Please mark your PR as ready when you want it to be reviewed!

🐱

Caused by: #18157 was opened by zhxie.

see: more, trace.

@repokitteh-read-only
Copy link
Copy Markdown

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/envoy/.
envoyproxy/api-shepherds assignee is @adisuissa
CC @envoyproxy/api-watchers: FYI only for changes made to api/envoy/.

🐱

Caused by: #18157 was opened by zhxie.

see: more, trace.

@zhxie zhxie force-pushed the header-lrl branch 2 times, most recently from f532236 to 79e0f66 Compare September 18, 2021 04:58
@zhxie zhxie marked this pull request as ready for review September 22, 2021 04:52
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you always give init_fill_time = true. Do we need the param in the first place?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, in local rate limit network filter, init_fill_time = false. A test of local rate limit network filter will throw an exception for different time system if we initialize fill_time_ in LocalRateLimiterImpl for local rate limit network filter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know why this is the case? What is the implication of not initializing the fill time at ctor time?

Copy link
Copy Markdown
Member

@tyxia tyxia Jan 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though this is used for calculating the default per-route RateLimit_Reset which is new header field for HTTP only, I am interested to learn why it fails in network filter? This code seems not to be something HTTP filter specific?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it is a mistake, and I suppose it is because I was not not familiar about the time system in the test before.

There is a assert failure threw by the test shows there is an inconsistent time system. And today I find the problem is only in the test code, not in the source code.

But I cannot fix the test. There is a conflict between the MockDispatcher which uses the GlobalTimeSystem and the MockStreamInfo in MockReadFilterCallbacks which uses the SimulatedTimeSystem. Do you have some ideas?

Copy link
Copy Markdown
Member

@tyxia tyxia Jan 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is already a TODO(#4160) in class GlobalTimeSystem: Switch default testSystem to SimulatedTimeSystem. I guess switching to that system might resolve the issue? I don't have the context on that TODO though.

If that is a lot of work to do or if that doesn't work, a quick workaround off top of my head (just for ideas not necessary to be the final solution): wrapping this line under if condition like below:

if (!descriptors.empty()) {
    tokens_.fill_time_ = time_source_.monotonicTime();
  }

This implicitly limits this code path to local descriptor override case which is only available in HTTP filter.
It is a bit hacky workaround :) but, IMO, it is still better than having bool flag in the interface all over the places.

Copy link
Copy Markdown
Contributor Author

@zhxie zhxie Jan 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I do not think it might work. In fact, a HTTP filter should load time to fill_time_ while its descriptors are empty.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I Imagine we don't need to have optional here. Do you consider just avoiding push(abls::nullopt)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need an optional here. In any request the filter is not enabled, we will not attach x-ratelimit-* headers.

Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this!
Left an API comment.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this enum different than:

enum XRateLimitHeadersRFCVersion {
// X-RateLimit headers disabled.
OFF = 0;
// Use `draft RFC Version 03 <https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html>`_.
DRAFT_VERSION_03 = 1;
}
?
I think we should use the other enum, and not redefine a new one as this can cause a divergence in the future (e.g., global RL supporting draft 03, while local RL supporting draft 04). We should make this consistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRAFT_VERSION_03 here means the responding x-ratelimit-* headers are according to draft RFC version 03. I think we should keep its name. And do you think it is a good idea for us to extract XRateLimitHeadersRFCVersion in LocalRateLimit and RateLimit and relocate it in common/ratelimit/v3/ratelimit.proto?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind the name DRAFT_VERSION_03.

Yes, I do think it would be better to extract the enum to the extension's common ratelimit proto.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An API change has been committed, but the CI reports the change is non-backwards-compatible. Is this a issue that needs to be fixed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generated_api_shadow directory was removed in #18091.
Please remove this file

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, recent commits have been merged.

Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some API comments.

"envoy.config.filter.http.rate_limit.v2.RateLimit";

// Defines the version of the standard to use for X-RateLimit headers.
enum XRateLimitHeadersRFCVersion {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be deprecated.

//
// Disabled by default.
XRateLimitHeadersRFCVersion enable_x_ratelimit_headers = 8
common.ratelimit.v3.XRateLimitHeadersRFCVersion enable_x_ratelimit_headers = 8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this breaks backwards compatibility, and therefore the field must be deprecated and a new field with the new type should be introduced.

@htuch extracting the enum to a common place (see: api/envoy/extensions/common/ratelimit/v3/ratelimit.proto) is arguably the right way to go, but will cause some deprecation churn. Another option is to make the local_rate_limit proto use the enum directly from the (global) rate_limit proto. WDYT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for the local rate limiting, put it in the common location and use it there. Either deprecate or eave a TODO to cleanup for global. I'm not super keen on API dependencies between extensions, since we're creating an unnecessary dependency in build/protos.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, if we treat local rate limit and rate limit as two independent extensions (like if we can decouple envoy and extensions someday in the future), both local rate limit and rate limit should maintain their own enum values. But if we consider the local rate limit and rate limit are inseparable, extract common enum is a good choice.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My take on this is that similar to the original issue (#15293) that requested a local rate-limiting feature support similar to what the global rate-limiting does, then in the future they should be in-sync.
I think that adding the enum to the common proto (as you've already done) is a good thing, and to avoid changes to the global rate-limiting API (following @htuch suggestion), we should not deprecate this enum at this stage, but add a [#next-major-version: comment (see: example here).

Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
@zhxie
Copy link
Copy Markdown
Contributor Author

zhxie commented Sep 30, 2021

/retest

@repokitteh-read-only
Copy link
Copy Markdown

Retrying Azure Pipelines:
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #18157 (comment) was created by @zhxie.

see: more, trace.

Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
@junr03 junr03 removed their assignment Feb 2, 2022
Copy link
Copy Markdown
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nits but otherwise this looks good to me

/wait

: requestAllowedHelper(tokens_);
}

uint32_t LocalRateLimiterImpl::maxTokens(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tend to favor longer names if it helps readability, but it's probably ok as is

*descriptors_.Add());
initializeWithDescriptor(std::chrono::milliseconds(3000), 2, 2);

/// 2 -> 1 tokens
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra /

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I will fix it.


TEST_F(FilterTest, Runtime) {
setup(fmt::format(config_yaml, "1", "false"), false, false);
setup(fmt::format(config_yaml, "1", "false", "\"OFF\""), false, false);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to include the quotes in there? You don't when you specify the other option

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we cannot remove these quotes. We write YAML like

local_rate_limit_per_downstream_connection: false
enable_x_ratelimit_headers: "OFF"

, so we must keep these quotes.

@yanavlasov yanavlasov self-assigned this Feb 8, 2022
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
snowp
snowp previously approved these changes Feb 17, 2022
Copy link
Copy Markdown
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@snowp
Copy link
Copy Markdown
Contributor

snowp commented Feb 17, 2022

@adisuissa mind doing the API sign off?

Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm api

// Defines the version of the standard to use for X-RateLimit headers.
// This field is deprecated in favor of :ref:`XRateLimitHeadersRFCVersion <envoy_v3_api_enum_extensions.common.ratelimit.v3.XRateLimitHeadersRFCVersion>`.
enum XRateLimitHeadersRFCVersion {
option deprecated = true;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on the fence whether this should be deprecated while enable_x_ratelimit_headers isn't deprecated and a new alternative isn't introduced (that is, both should either be deprecated or not).
I guess that for now you can remove the deprecation, and add #next-major-version to your comment above.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we are going to unify the XRateLimitHeadersRFCVersion used by rate limit and local rate limit. I will change the deprecated to a #next-major-version.

for (const auto& request_descriptor : request_descriptors) {
auto it = descriptors_.find(request_descriptor);
if (it != descriptors_.end()) {
return static_cast<uint32_t>(absl::ToInt64Seconds(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some expected bound to the result of ToInt64Seconds(...)? Could it overflow or cause issues with the cast to uint32_t?

Copy link
Copy Markdown
Contributor Author

@zhxie zhxie Feb 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the return type from uint32_t to int64_t in previous commits, so we do not need the cast anymore.

Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
@zhxie
Copy link
Copy Markdown
Contributor Author

zhxie commented Feb 28, 2022

/retest

@repokitteh-read-only
Copy link
Copy Markdown

Retrying Azure Pipelines:
Retried failed jobs in: envoy-presubmit

🐱

Caused by: a #18157 (comment) was created by @zhxie.

see: more, trace.

@rojkov
Copy link
Copy Markdown
Member

rojkov commented Mar 3, 2022

@adisuissa could you please reinstate the API approval?

Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm api

@adisuissa adisuissa merged commit 5c56e45 into envoyproxy:main Mar 10, 2022
JuniorHsu pushed a commit to JuniorHsu/envoy that referenced this pull request Mar 17, 2022
…ng (envoyproxy#18157)

ratelimit: add support for x-ratelimit-* headers in local rate limiting

Signed-off-by: Xie Zhihao <zhihao.xie@intel.com>
Signed-off-by: kuochunghsu <kuochunghsu@pinterest.com>
@zhxie zhxie deleted the header-lrl branch September 7, 2022 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for x-ratelimit-* headers in local rate limiting